/**
 * @author ens12ilt - ens12ple
 */

package utils;

import java.util.ArrayList;

import log.Log;
import model.Image;
import model.Position;
import model.Subset;

public final class ImageProcessing {
	
	private static final String LOG_TAG = ImageProcessing.class.getSimpleName();

	private static final int WHITE = 0;
	private static final int BLACK = 31;

	/**
	 * 
	 * @param image image to process
	 * @param threshold the average threshold for this figure
	 */
	public static void erode(Image image, int threshold){
		int[][] img = image.getImage();
		for(int i = 1; i < img.length - 1; i++){
			for(int j = 1; j < img[i].length - 1; j++){
				if (i>0 && j>0 && i+1<img.length && j+1<img[i].length && (img[i-1][j] >= threshold  || img[i][j-1] >= threshold ||  img[i+1][j] >= threshold  || img[i][j+1] >= threshold)){
					img[i][j] = WHITE;
				}

			}
		}
	}

	/**
	 * 
	 * @param image image to process
	 * @param threshold the average threshold for this figure
	 */
	public static void binarize(Image img, int threshold){
		int[][] toBinarize = img.getImage();
		for(int i = 0; i < toBinarize.length; i++){
			for(int j = 0; j < toBinarize[i].length; j++){
				toBinarize[i][j] = (toBinarize[i][j] < threshold) ? WHITE : BLACK;
			}
		}
	}

	/**
	 * 
	 * @param image image to process
	 * @param threshold the average threshold for this figure
	 */
	public static void dilate(Image image, int threshold){
		int[][] img = image.getImage();
		for (int i = 1; i<img.length -1; i++){
			for (int j=1; j<img[i].length -1; j++){
				if (img[i][j] >=  threshold){
					img[i-1][j] = BLACK;
				}
			}
		}
	}

	/**
	 * 
	 * @param image1 the main image
	 * @param image2 the subtracted image
	 */
	public static void diff(Image image1, Image image2){
		int[][] img1 = image1.getImage();
		int[][] img2 = image2.getImage();
		for(int i = 0; i < img1.length; i++){
			for(int j = 0; j < img1[i].length; j++){
				if(img2[i][j] == BLACK){
					img1[i][j] = WHITE;
				}
			}
		}
	}

	/**
	 * 
	 * @param i image to process
	 * @param numberOfIteration number of iteration for the cleaning algorithm
	 */
	public static void cleanImage(Image i, int numberOfIteration){
		int threshold;
		for(int j = 0; j < numberOfIteration; j++){
			Image cpy = new Image(i);
			threshold = i.getAverageThreshold();
			ImageProcessing.erode(cpy, threshold);
			ImageProcessing.binarize(cpy, threshold);
			ImageProcessing.diff(i, cpy);
		}
		int[] center = {0, 0};
		int[][] rotated = rotate(i, center);
		i.setImage(rotated);
		int[][] fin = cropCenter(i, 20, 20);
		i.setImage(fin);
		ArrayList<Subset> subsetsTmp = i.getPositionsForMaxValues();
		int[] electedSubsets = i.getLargerSubsets(subsetsTmp);
		ArrayList<Subset> subsets = new ArrayList<Subset>();
		subsets.add(subsetsTmp.get(electedSubsets[0]));
		subsets.add(subsetsTmp.get(electedSubsets[1]));
		boolean needReverse = false;
		int count = 0;
		int sizeSum = 0;
		for(Subset s : subsets){
			for(Position p : s.getSet()){
				if(p.getY() > 10){
					count++;
				}
			}
			sizeSum += s.getSet().size();
		}
		if(count > sizeSum/2){
			needReverse = true;
		}
		
		if(needReverse){
			int[][] newImage = new int[20][20];
			for(int y = 19; y >= 0;y--){
				for(int x = 0; x < 20; x++){
					newImage[19-y][x] = i.getImage()[y][x];
				}
			}
			i.setImage(newImage);
		}
		
	}
	
	/**
	 * 
	 * @param img image to process
	 * @param width desired image width
	 * @param height desired image height
	 * @return the image recentered
	 */
	public static int[][] cropCenter(Image img, int width, int height){
		int[][] newImage = new int[height][width];
		int startX = img.getImage()[0].length/2 - width/2;
		int startY = img.getImage().length/2 - height/2;
		for(int y = startY; y < startY+width - 1; y++){
			for(int x = startX; x < startX+height - 1; x++){
				newImage[y - startY][x - startX] = img.getImage()[y][x];
			}
		}
		return newImage;
	}
	
	/**
	 * 
	 * @param img image to process
	 * @param center rotation center
	 * @return
	 */
	public static int[][] rotate(Image img, int[] center){
		int[] x1 = {1, 0};
		int[][] image = img.getImage();
		int[] vMe = img.createVectorFromMiddleOfEyes();
		double angle = -VectorUtils.getAngle(x1, vMe);
		Log.i(LOG_TAG, Math.toDegrees(angle) + "");
		double cosAngle = Math.cos(angle);
		double sinAngle = Math.sin(angle);
		int newWidth = (int) (image[0].length * Math.abs(cosAngle) + image.length * Math.abs(sinAngle));
		int newHeight = (int) (image[0].length * Math.abs(sinAngle) + image.length * Math.abs(cosAngle));
		int[][] rotatedImage = new int[newHeight][newWidth];
		int initX;
		int initY;
		int outCenterX = newWidth/2;
		int outCenterY = newHeight/2;
		for(int y = 0; y < newHeight; y++){
			for(int x = 0; x < newWidth; x++){
				initX = (int) ((x-outCenterX)*cosAngle + (y-outCenterY) * sinAngle + 10);
				initY = (int) (-(x-outCenterX)*sinAngle + (y-outCenterY)*cosAngle  + 10);
				if(initX >= 0 && initX <= 19 && initY >= 0 && initY <= 19){
					rotatedImage[y][x] = image[initY][initX];
				}else{
					rotatedImage[y][x] = 0;
				}
			}
		}
		return rotatedImage;
	}
}
